﻿using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using DZTC.Base32Utility.Core;
using DZTC.Base32Utility.Core.Base32;

namespace DZTC.Base32Utility
{
    public partial class ConversionForm : Form
    {
        private string InputFile { get; set; }
        private string OutputFile { get; set; }
        private ConversionDirection Direction { get; set; }

        private BackgroundOperation ConversionOperation { get; set; }

        private void ApplyText()
        {
            var progressText = String.Format("Converting {0} to {1} ...",
                                            Path.GetFileName(InputFile),
                                            Path.GetFileName(OutputFile));

            if (progressText.Length > 80)
                progressText = progressText.Substring(0, 80) + "...";

            gbProgress.Text = progressText;
        }

        private ConversionForm(string inputFile, string outputFile, ConversionDirection direction)
        {
            InitializeComponent();

            ConversionOperation =
                new BackgroundOperation(new BackgroundOperation.OperationVoidDelegate(PerformConversion));
            ConversionOperation.Complete += ConversionOperationCompleted;
            ConversionOperation.ProgressChanged += UpdateProgress;

            InputFile = inputFile;
            OutputFile = outputFile;
            Direction = direction;

           ApplyText();
        }


        private void UpdateProgress(long value, long max)
        {
            pbConversionProgress.Value = (int) Math.Round(100*(double) value/max);
        }

        private void ConversionOperationCompleted(BackgroundOperation sender,
                                                  BackgroundOperation.CompleteEventArgs args)
        {
            try
            {
                args.QueryResult();
            }
            catch (OperationCanceledException)
            {
                // okay
            }
            catch (FileNotFoundException ex)
            {
                Dialog.Error("File not found", ex.Message);
            }
            catch (IOException ex)
            {
                Dialog.Error("IO error", ex.Message);
            }
            catch (UnauthorizedAccessException ex)
            {
                Dialog.Error("Access error", ex.Message);
            }
            catch (Base32Exception ex)
            {
                Dialog.Error("BASE32 converter error", ex.Message);
            }
            catch (Exception ex)
            {
                Dialog.Error("Error", ex.Message);
            }

            CloseForm();
        }

        private void TryDeleteFile(string fileName)
        {
            try
            {
                File.Delete(OutputFile);
            }
            catch
            {
                // supress
            }
        }

        private void PerformConversion(object arg, CancellationToken ct, ProgressReporter pr)
        {
            var outputBackuped = false;
            var tmpName = Path.Combine(Path.GetDirectoryName(OutputFile), 
                Guid.NewGuid().ToString("n"));

            try
            {
                if (File.Exists(OutputFile))
                {
                    File.Move(OutputFile, tmpName);
                    outputBackuped = true;
                }

                using (var input = File.Open(InputFile, FileMode.Open, FileAccess.Read, FileShare.Read))
                using (var output = File.Open(OutputFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
                {
                    if (Direction == ConversionDirection.Decode)
                    {
                        Base32Decoder.Decode(input, output, ct, pr);
                    }
                    else
                    {
                        Base32Encoder.Encode(input, output, ct, pr);
                    }
                }

                if (outputBackuped)
                {
                    File.Delete(tmpName);
                }
            }
            catch (Exception)
            {
                TryDeleteFile(OutputFile);
                if (outputBackuped)
                    File.Move(tmpName, OutputFile);
                

                throw;
            }
        }

        public static void Run(string inputFile, string outputFile, ConversionDirection direction)
        {
            var frm = new ConversionForm(inputFile, outputFile, direction);
            frm.ShowDialog();
        }

        private void CancelClick(object sender, EventArgs e)
        {
            ConversionOperation.Cancel();
        }

        private void FormShown(object sender, EventArgs e)
        {
            ConversionOperation.Start(null);
        }

        private void CloseForm()
        {
            CloseCausedNotByUser = true;
            Close();
            CloseCausedNotByUser = false;
        }

        private bool CloseCausedNotByUser { get; set; }

        private void OnFormClose(object sender, FormClosingEventArgs e)
        {
            if (CloseCausedNotByUser) return;
            e.Cancel = true;

            ConversionOperation.Cancel();
        }
    }
}